#include "stdafx.h"

#include "bochs.h"
#include "softfloatx80.h"
#include "softfloat-specialize.h"

extern float_status_t FPU_pre_exception_handling(Bit16u control_word);

/* D9 F0 */
void IA32_CPU::F2XM1(Ia32_Instruction_c *i)
{
   prepareFPU(i);

  clear_C1();

  if (IS_TAG_EMPTY(0))
  {
      FPU_stack_underflow(0);
     return;
  }

  float_status_t status = 
	FPU_pre_exception_handling( the_i387.get_control_word() | FPU_PR_80_BITS);

  floatx80 result = f2xm1(IA32_READ_FPU_REG(0), status);

  if ( FPU_exception(status.float_exception_flags))
      return;

  IA32_WRITE_FPU_REG(result, 0);
}

/* D9 F1 */
void IA32_CPU::FYL2X(Ia32_Instruction_c *i)
{
   prepareFPU(i);

  clear_C1();

  if (IS_TAG_EMPTY(0) || IS_TAG_EMPTY(1))
  {
      FPU_stack_underflow(0, 1);
     return;
  }

  float_status_t status = 
	FPU_pre_exception_handling( the_i387.get_control_word() | FPU_PR_80_BITS);

  floatx80 result = fyl2x(IA32_READ_FPU_REG(0), IA32_READ_FPU_REG(1), status);

  if ( FPU_exception(status.float_exception_flags))
      return;

   the_i387.FPU_pop();
  IA32_WRITE_FPU_REG(result, 0);
}

/* D9 F2 */
void IA32_CPU::FPTAN(Ia32_Instruction_c *i)
{
   prepareFPU(i);

  clear_C1();
  clear_C2();

  if (! IS_TAG_EMPTY(-1) || IS_TAG_EMPTY(0))
  {
       FPU_exception(FPU_EX_Stack_Overflow);

      /* The masked response */
      if ( the_i387.is_IA_masked())
      {
          IA32_WRITE_FPU_REGISTER_AND_TAG(floatx80_default_nan, FPU_Tag_Special, 0);
           the_i387.FPU_push();
          IA32_WRITE_FPU_REGISTER_AND_TAG(floatx80_default_nan, FPU_Tag_Special, 0);
      }

      return; 
  }

  extern const floatx80 Const_1;

  float_status_t status = 
	FPU_pre_exception_handling( the_i387.get_control_word() | FPU_PR_80_BITS);

  floatx80 y = IA32_READ_FPU_REG(0);
  if (ftan(y, status) == -1)
  {
      FPU_PARTIAL_STATUS |= FPU_SW_C2;
      return;
  }

  if (floatx80_is_nan(y))
  {
      if (!  FPU_exception(status.float_exception_flags))
      {
          IA32_WRITE_FPU_REGISTER_AND_TAG(y, FPU_Tag_Special, 0);
           the_i387.FPU_push();
          IA32_WRITE_FPU_REGISTER_AND_TAG(y, FPU_Tag_Special, 0);
      }

      return;
  }

  if ( FPU_exception(status.float_exception_flags))
      return;

  IA32_WRITE_FPU_REG(y, 0);
   the_i387.FPU_push();
  IA32_WRITE_FPU_REGISTER_AND_TAG(Const_1, FPU_Tag_Valid, 0);
}

/* D9 F3 */
void IA32_CPU::FPATAN(Ia32_Instruction_c *i)
{
  clear_C1();

  if (IS_TAG_EMPTY(0) || IS_TAG_EMPTY(1))
  {
      FPU_stack_underflow(0, 1);
     return;
  }

  float_status_t status = 
	FPU_pre_exception_handling( the_i387.get_control_word() | FPU_PR_80_BITS);

  floatx80 result = fpatan(IA32_READ_FPU_REG(0), IA32_READ_FPU_REG(1), status);

  if ( FPU_exception(status.float_exception_flags))
      return;

   the_i387.FPU_pop();
  IA32_WRITE_FPU_REG(result, 0);
}

/* D9 F4 */
void IA32_CPU::FXTRACT(Ia32_Instruction_c *i)
{
   prepareFPU(i);

  clear_C1();

  if (! IS_TAG_EMPTY(-1) || IS_TAG_EMPTY(0))
  {
       FPU_exception(FPU_EX_Stack_Overflow);

      /* The masked response */
      if ( the_i387.is_IA_masked())
      {
          IA32_WRITE_FPU_REGISTER_AND_TAG(floatx80_default_nan, FPU_Tag_Special, 0);
           the_i387.FPU_push();
          IA32_WRITE_FPU_REGISTER_AND_TAG(floatx80_default_nan, FPU_Tag_Special, 0);
      }

      return; 
  }

  float_status_t status = 
      FPU_pre_exception_handling( the_i387.get_control_word());

  floatx80 a = IA32_READ_FPU_REG(0);
  floatx80 b = floatx80_extract(a, status);

  if ( FPU_exception(status.float_exception_flags))
      return;

  IA32_WRITE_FPU_REG(b, 0);	// exponent
   the_i387.FPU_push();
  IA32_WRITE_FPU_REG(a, 0);	// fraction
}

/* D9 F5 */
void IA32_CPU::FPREM1(Ia32_Instruction_c *i)
{
   prepareFPU(i);

  clear_C1();

  if (IS_TAG_EMPTY(0) || IS_TAG_EMPTY(1))
  {
      FPU_stack_underflow(0);
     return;
  }

  float_status_t status = 
	FPU_pre_exception_handling( the_i387.get_control_word());

  Bit64u quotient;

  floatx80 a = IA32_READ_FPU_REG(0);
  floatx80 b = IA32_READ_FPU_REG(1);

  floatx80 result = floatx80_ieee754_remainder(a, b, quotient, status);

  if ( FPU_exception(status.float_exception_flags))
      return;

  int cc = 0;
  if (quotient == (Bit64u) -1) cc = FPU_SW_C2;
  else
  {
      if (quotient & 1) cc |= FPU_SW_C1;
      if (quotient & 2) cc |= FPU_SW_C3;
      if (quotient & 4) cc |= FPU_SW_C0;
  }
  setcc(cc);

  IA32_WRITE_FPU_REG(result, 0);
}

/* D9 F8 */
void IA32_CPU::FPREM(Ia32_Instruction_c *i)
{
   prepareFPU(i);

  clear_C1();

  if (IS_TAG_EMPTY(0) || IS_TAG_EMPTY(1))
  {
      FPU_stack_underflow(0);
     return;
  }

  float_status_t status = 
	FPU_pre_exception_handling( the_i387.get_control_word());

  Bit64u quotient;

  floatx80 a = IA32_READ_FPU_REG(0);
  floatx80 b = IA32_READ_FPU_REG(1);

  floatx80 result = floatx80_remainder(a, b, quotient, status);

  if ( FPU_exception(status.float_exception_flags))
      return;

  int cc = 0;
  if (quotient == (Bit64u) -1) cc = FPU_SW_C2;
  else
  {
      if (quotient & 1) cc |= FPU_SW_C1;
      if (quotient & 2) cc |= FPU_SW_C3;
      if (quotient & 4) cc |= FPU_SW_C0;
  }
  setcc(cc);

  IA32_WRITE_FPU_REG(result, 0);
}

/* D9 F9 */
void IA32_CPU::FYL2XP1(Ia32_Instruction_c *i)
{
   prepareFPU(i);

  clear_C1();

  if (IS_TAG_EMPTY(0) || IS_TAG_EMPTY(1))
  {
      FPU_stack_underflow(0, 1);
     return;
  }

  float_status_t status = 
	FPU_pre_exception_handling( the_i387.get_control_word() | FPU_PR_80_BITS);

  floatx80 result = fyl2xp1(IA32_READ_FPU_REG(0), IA32_READ_FPU_REG(1), status);

  if ( FPU_exception(status.float_exception_flags))
      return;

   the_i387.FPU_pop();
  IA32_WRITE_FPU_REG(result, 0);
}

/* D9 FB */
void IA32_CPU::FSINCOS(Ia32_Instruction_c *i)
{
   prepareFPU(i);

  clear_C1();
  clear_C2();

  if (! IS_TAG_EMPTY(-1) || IS_TAG_EMPTY(0))
  {
       FPU_exception(FPU_EX_Stack_Overflow);

      /* The masked response */
      if ( the_i387.is_IA_masked())
      {
          IA32_WRITE_FPU_REGISTER_AND_TAG(floatx80_default_nan, FPU_Tag_Special, 0);
           the_i387.FPU_push();
          IA32_WRITE_FPU_REGISTER_AND_TAG(floatx80_default_nan, FPU_Tag_Special, 0);
      }

      return; 
  }

  float_status_t status = 
	FPU_pre_exception_handling( the_i387.get_control_word() | FPU_PR_80_BITS);

  floatx80 y = IA32_READ_FPU_REG(0);
  floatx80 sin_y, cos_y;
  if (fsincos(y, &sin_y, &cos_y, status) == -1) 
  {
      FPU_PARTIAL_STATUS |= FPU_SW_C2;
      return;
  }

  if ( FPU_exception(status.float_exception_flags))
      return;

  IA32_WRITE_FPU_REG(sin_y, 0);
   the_i387.FPU_push();
  IA32_WRITE_FPU_REG(cos_y, 0);
}

/* D9 FD */
void IA32_CPU::FSCALE(Ia32_Instruction_c *i)
{
   prepareFPU(i);

  clear_C1();

  if (IS_TAG_EMPTY(0) || IS_TAG_EMPTY(1))
  {
      FPU_stack_underflow(0);
     return;
  }

  float_status_t status = 
	FPU_pre_exception_handling( the_i387.get_control_word());

  floatx80 result = floatx80_scale(IA32_READ_FPU_REG(0), IA32_READ_FPU_REG(1), status);

  if ( FPU_exception(status.float_exception_flags))
      return;

  IA32_WRITE_FPU_REG(result, 0);
}

/* D9 FE */
void IA32_CPU::FSIN(Ia32_Instruction_c *i)
{
   prepareFPU(i);

  clear_C1();
  clear_C2();

  if (IS_TAG_EMPTY(0))
  {
      FPU_stack_underflow(0);
     return;
  }

  float_status_t status = 
	FPU_pre_exception_handling( the_i387.get_control_word() | FPU_PR_80_BITS);

  floatx80 y = IA32_READ_FPU_REG(0);
  if (fsin(y, status) == -1)
  {
      FPU_PARTIAL_STATUS |= FPU_SW_C2;
      return;
  }

  if ( FPU_exception(status.float_exception_flags))
      return;

  IA32_WRITE_FPU_REG(y, 0);
}

/* D9 FF */
void IA32_CPU::FCOS(Ia32_Instruction_c *i)
{
   prepareFPU(i);

  clear_C1();
  clear_C2();

  if (IS_TAG_EMPTY(0))
  {
      FPU_stack_underflow(0);
     return;
  }

  float_status_t status = 
	FPU_pre_exception_handling( the_i387.get_control_word() | FPU_PR_80_BITS);

  floatx80 y = IA32_READ_FPU_REG(0);
  if (fcos(y, status) == -1)
  {
      FPU_PARTIAL_STATUS |= FPU_SW_C2;
      return;
  }

  if ( FPU_exception(status.float_exception_flags))
      return;

  IA32_WRITE_FPU_REG(y, 0);
}
